import json
import time

from nextcloud import NextCloud  # 第三方 api
from rest_framework import status

from api import exceptions
from buckets.models import NextCloudExternalMounts, NextCloudExternalApplicable, NextCloudExternalConfig, \
    NextCloudExternalOptions, NextcloudMounts, NextcloudOcUsers
from buckets.utils import NextCloudExternalManagement
from ceph.models import GlobalConfig


class NextcloudDBError(exceptions.Error):
    """"""
    pass


class NextcloudDBUserError(exceptions.Error):
    """"""
    default_status_code = status.HTTP_500_INTERNAL_SERVER_ERROR


class NextcloudHandler:

    def get_nextcloud_bucket(self, bucket_id):
        """ 获取 next cloud 挂载存储桶列表

        :param bucket_id:
        """
        return NextcloudMounts.objects.filter(bucket_id=bucket_id).first()

    def get_nextcloud_bucket_by_uid(self, uid):
        """ 获取 next cloud 挂载存储桶列表

        :param uid: nextcloud id
        """
        return NextcloudMounts.objects.filter(mount_uid=uid).all()

    def get_nextcloud_admin(self):
        next_user = None
        next_pwd = None
        url = None
        next_admin = GlobalConfig.objects.filter(name__in=[GlobalConfig.ConfigName.NEXTCLOUD_ADMIN_ACCOUNT.value, GlobalConfig.ConfigName.NEXTCLOUD_SERVICE_URL.value])
        for next_ in next_admin:
            if next_.name == GlobalConfig.ConfigName.NEXTCLOUD_ADMIN_ACCOUNT.value:

                next_user, next_pwd = next_.content.split(':')

            if next_.name == GlobalConfig.ConfigName.NEXTCLOUD_SERVICE_URL.value:
                if not next_.content:
                    raise Exception(f'请设置 nextCloudServiceUrl 内容')
                # 考虑多个地址 内网地址
                urls = None
                if  '，' in next_.content:
                    raise Exception(f' 站点参数中 nextCloudServiceUrl 内容有中文"，" 请修改。')

                if ',' in next_.content:
                    if next_.content.endswith(','):
                        urls = next_.content[:-1].split(',')
                    else:
                        urls = next_.content.split(',')
                else:
                    urls = GlobalConfig.parse_lines(text=next_.content)

                if len(urls) > 1:
                    url = urls[1]
                else:
                    url = urls[0]

        if next_user and next_pwd and url:
            return url, next_user, next_pwd

        raise Exception(f'获取到的nextcloud管理员信息和nextcloud服务有误,请联系管理员解决问题')

    def delete_nextcloud_user_pwd(self, uid):
        """删除 nextcloud 用户密码"""

        # NextcloudOcUsers

        nc_db_user = NextCloudExternalManagement(table_name=NextcloudOcUsers.Meta.db_table,
                                                 nextcloud_class=NextcloudOcUsers)

        nc_db_user_class = nc_db_user.get_obj_model_class()
        nc_db_user_obj = nc_db_user_class.objects.filter(uid=uid).first()
        if nc_db_user_obj:
            nc_db_user_obj.password = ''
            nc_db_user_obj.save(update_fields=['password'])
            return True

        raise Exception(f'未找到 nextcloud 用户信息')

    def create_nextcloud_accounts(self, uid, url, next_user, next_pwd):
        """创建 nextcloud 用户"""
        # url, next_user, next_pwd = self.get_nextcloud_admin()

        nxc = NextCloud(endpoint=url, user=next_user, password=next_pwd)
        add_user_res = nxc.add_user(uid, str(time.time()))
        if add_user_res.status_code != 100:
            raise
        # 删除用户密码
        try:
            self.delete_nextcloud_user_pwd(uid)
        except Exception as e:
            pass  # 先保证使用正常

        return uid

    def delete_nextcloud_accounts(self, uid):
        """删除 nextcloud 用户"""
        url, next_user, next_pwd = self.get_nextcloud_admin()

        if next_user and next_pwd and url:
            """ """
            nxc = NextCloud(endpoint=url, user=next_user, password=next_pwd)
            delete_user_res = nxc.delete_user(uid)
            if delete_user_res.status_code != 100:
                status = delete_user_res.json_data['ocs']['meta']['status']
                if status == 'failure':
                    raise Exception(f'删除nextcloud 用户失败, 请重试')

                raise Exception(f'删除nextcloud 用户失败, err:{delete_user_res.json_data}')

            return True

        raise Exception(f'nextcloud 相关信息设置有问题。')

    def get_nextcloud_user(self, aai_id):

        try:
            url, next_user, next_pwd = self.get_nextcloud_admin()
        except Exception as e:
            raise e

        nxc = NextCloud(endpoint=url, user=next_user, password=next_pwd)
        uid = f'CSTCloudAAI-{aai_id}'

        user_res = nxc.get_user(uid)
        if user_res.status_code != 100:
            user_exist_err = user_res.json_data['ocs']['meta']['message']
            if user_exist_err == 'User does not exist':
                return self.create_nextcloud_accounts(uid, url, next_user, next_pwd)

            raise Exception(f'查询nextcloud 用户时请求错误')

        return uid

    # def select_user_email(self, email: str):
    #     nc_db_accounts_data = NextCloudExternalManagement(table_name=NextCloudAccountsData.Meta.db_table,
    #                                                       nextcloud_class=NextCloudAccountsData)
    #
    #     # 查询信息
    #     accountsdata_class = nc_db_accounts_data.get_obj_model_class()
    #     accountsdata_obj = accountsdata_class.objects.filter(name='email', value=email).first()
    #
    #     if accountsdata_obj:
    #         return accountsdata_obj
    #
    #     nestcloud_server_url = GlobalConfig.objects.filter(name='nextCloudServiceUrl').first()
    #     if nestcloud_server_url:
    #         raise NextcloudDBUserError(f'用户未登录nextcloud 网盘服务({nestcloud_server_url.value}), 请登录后再次尝试。')
    #
    #     raise NextcloudDBUserError(f'用户未登录nextcloud 网盘服务，请联系管理员提供具体服务地址。')

    def add_oc_external_mounts(self, bucket_name):
        # 挂载表 oc_external_mounts  可重复添加 其他表看id
        nc_db_mounts = NextCloudExternalManagement(table_name=NextCloudExternalMounts.Meta.db_table,
                                                   nextcloud_class=NextCloudExternalMounts)

        # 创建信息
        mounts_class = nc_db_mounts.get_obj_model_class()
        mounts_obj = mounts_class(mount_point=f'/{bucket_name}', storage_backend='amazons3',
                                  auth_backend='amazons3::accesskey', priority=100, type=2)

        try:
            mounts_obj.save()
        except Exception as e:
            raise NextcloudDBError(
                message=f'nextcloud 在 external_mounts 新建对象元数据失败，数据库错误, {str(e)}')

        return mounts_obj

    def add_oc_external_applicable(self, mount_id, nextcloud_user_id: str):

        #  oc_external_applicable
        nc_db_applicable = NextCloudExternalManagement(table_name=NextCloudExternalApplicable.Meta.db_table,
                                                       nextcloud_class=NextCloudExternalApplicable)

        # 创建信息
        nc_db_applicable_calss = nc_db_applicable.get_obj_model_class()

        try:
            model_obj = nc_db_applicable_calss(mount_id=mount_id, type=3, value=nextcloud_user_id)
            model_obj.save()
        except Exception as e:
            raise NextcloudDBError(message=f'新建对象元数据失败，数据库错误, {str(e)}')

        return model_obj

    def add_oc_external_config(self, mount_id, bucket_name, nextcloud_s3_url, key, secret, port='', region='',
                               storageClass='', use_ssl='', use_path_style='1', legacy_auth='', useMultipartCopy='1',
                               ):

        #  oc_external_config
        nc_db_config = NextCloudExternalManagement(table_name=NextCloudExternalConfig.Meta.db_table,
                                                   nextcloud_class=NextCloudExternalConfig)

        # 创建信息
        nc_db_config_calss = nc_db_config.get_obj_model_class()

        try:
            model_obj = nc_db_config_calss.objects.bulk_create([
                nc_db_config_calss(mount_id=mount_id, key='bucket', value=bucket_name),
                nc_db_config_calss(mount_id=mount_id, key='hostname', value=nextcloud_s3_url),
                nc_db_config_calss(mount_id=mount_id, key='port', value=port),
                nc_db_config_calss(mount_id=mount_id, key='region', value=region),
                nc_db_config_calss(mount_id=mount_id, key='storageClass', value=storageClass),
                nc_db_config_calss(mount_id=mount_id, key='use_ssl', value=use_ssl),  # nextcloud  true:1 false:0
                nc_db_config_calss(mount_id=mount_id, key='use_path_style', value=use_path_style),
                # nextcloud  true:1 false:0
                nc_db_config_calss(mount_id=mount_id, key='legacy_auth', value=legacy_auth),
                nc_db_config_calss(mount_id=mount_id, key='useMultipartCopy', value=useMultipartCopy),
                # nextcloud  true:1 false:0
                nc_db_config_calss(mount_id=mount_id, key='key', value=key),
                nc_db_config_calss(mount_id=mount_id, key='secret', value=secret),
            ])
        except Exception as e:
            raise NextcloudDBError(message=f'新建对象元数据失败，数据库错误, {str(e)}')

        return model_obj

    def add_oc_external_options(self, mount_id, encrypt='true', previews='true', enable_sharing='false',
                                filesystem_check_changes='', encoding_compatibility='', readonly='false'):

        #  oc_external_options
        nc_db_options = NextCloudExternalManagement(table_name=NextCloudExternalOptions.Meta.db_table,
                                                    nextcloud_class=NextCloudExternalOptions)

        # 创建信息
        nc_db_options_calss = nc_db_options.get_obj_model_class()

        try:
            model_obj = nc_db_options_calss.objects.bulk_create([
                nc_db_options_calss(mount_id=mount_id, key='encrypt', value=encrypt),
                nc_db_options_calss(mount_id=mount_id, key='previews', value=previews),
                nc_db_options_calss(mount_id=mount_id, key='enable_sharing', value=enable_sharing),
                nc_db_options_calss(mount_id=mount_id, key='filesystem_check_changes', value=filesystem_check_changes),
                nc_db_options_calss(mount_id=mount_id, key='encoding_compatibility', value=encoding_compatibility),
                nc_db_options_calss(mount_id=mount_id, key='readonly', value=readonly),  # nextcloud  true:1 false:0
            ])
        except Exception as e:
            raise NextcloudDBError(message=f'新建对象元数据失败，数据库错误, {str(e)}')

        return model_obj

    def add_bucket_nextcloud(self, email, nextcloud_s3_url, bucket, s3_key, user, aai_id):

        try:
            nextcloud_user_id = self.get_nextcloud_user(aai_id=aai_id)
        except Exception as exc:
            raise exc

        try:
            mounts_obj = self.add_oc_external_mounts(bucket_name=bucket.name)
        except NextcloudDBError as exc:
            raise exc

        mounts_id = mounts_obj.mount_id

        try:
            applicable_obj = self.add_oc_external_applicable(mount_id=mounts_id, nextcloud_user_id=nextcloud_user_id)
        except NextcloudDBError as exc:
            raise exc

        try:
            config_obj = self.add_oc_external_config(mount_id=mounts_id, bucket_name=bucket.name,
                                                     nextcloud_s3_url=nextcloud_s3_url, key=s3_key.id,
                                                     secret=s3_key.secret_key)
        except NextcloudDBError as exc:
            raise exc

        try:
            options_obj = self.add_oc_external_options(mount_id=mounts_id)
        except NextcloudDBError as exc:
            raise exc

        try:
            NextcloudMounts.objects.create(
                bucket_name=bucket.name,
                bucket_id=bucket.id,
                mount_id=mounts_id,
                mount_uid=nextcloud_user_id,
                user_email=email,
                user_id=user.id
            )
        except Exception as e:
            exc = exceptions.BadRequest(f'信息已同步到nextcloud 服务中，数据表记录同步信息错误：{str(e)}。')
            raise exc

    def delete_oc_external_mounts(self, mount_id):
        nc_db_mounts = NextCloudExternalManagement(table_name=NextCloudExternalMounts.Meta.db_table,
                                                   nextcloud_class=NextCloudExternalMounts)
        mounts_class = nc_db_mounts.get_obj_model_class()

        mounts_obj = mounts_class.objects.filter(mount_id=mount_id).first()
        if not mounts_obj:
            return

        try:
            mounts_obj.delete()
        except Exception as e:
            # 必须有日志记录
            raise NextcloudDBError(
                message=f'nextcloud 在 external_mounts 删除对象元数据失败，数据库错误, {str(e)}')

    def delete_oc_external_applicable(self, nextcloud_user_id: str, bucket_name: str):
        nc_db_applicable = NextCloudExternalManagement(table_name=NextCloudExternalApplicable.Meta.db_table,
                                                       nextcloud_class=NextCloudExternalApplicable)

        # 创建信息
        nc_db_applicable_calss = nc_db_applicable.get_obj_model_class()
        model_obj = nc_db_applicable_calss.objects.filter(value=nextcloud_user_id,
                                                          mount__mount_point=f'/{bucket_name}').first()
        if not model_obj:
            return

        mount_id = model_obj.mount_id

        try:
            model_obj.delete()
        except Exception as e:
            raise NextcloudDBError(message=f'external_applicable 删除对象元数据失败，数据库错误, {str(e)}')

        return mount_id

    def delete_oc_external_config(self, mount_id: str):
        #  oc_external_config
        nc_db_config = NextCloudExternalManagement(table_name=NextCloudExternalConfig.Meta.db_table,
                                                   nextcloud_class=NextCloudExternalConfig)

        # 创建信息
        nc_db_config_calss = nc_db_config.get_obj_model_class()
        model_obj = nc_db_config_calss.objects.filter(mount_id=mount_id).all()
        if not model_obj:
            return

        try:
            model_obj.delete()
        except Exception as e:
            raise NextcloudDBError(message=f'nextcloud external_config 删除对象元数据失败，数据库错误, {str(e)}')

    def delete_oc_external_options(self, mount_id: str):
        #  oc_external_options
        nc_db_options = NextCloudExternalManagement(table_name=NextCloudExternalOptions.Meta.db_table,
                                                    nextcloud_class=NextCloudExternalOptions)

        # 创建信息
        nc_db_options_calss = nc_db_options.get_obj_model_class()

        model_obj = nc_db_options_calss.objects.filter(mount_id=mount_id).all()
        if not model_obj:
            return

        try:
            model_obj.delete()
        except Exception as e:
            raise NextcloudDBError(message=f'nextcloud external_options 删除对象元数据失败，数据库错误, {str(e)}')

    def delete_nextcloud_user(self, aai_id):
        """ 删除 nextcloud 用户"""

        uid = f'CSTCloudAAI-{aai_id}'
        obj = self.get_nextcloud_bucket_by_uid(uid=uid)
        if obj:
            raise Exception(f'部分桶还在nextcloud中,请将它们全部移除后在删除')

        return self.delete_nextcloud_accounts(uid)

    def delete_bucket_nextcloud(self, bucket):

        nc_obj = NextcloudMounts.objects.filter(bucket_name=bucket.name, user_id=bucket.user_id,
                                                bucket_id=bucket.id).first()

        if not nc_obj:
            exc = exceptions.BadRequest(f'未找到存储桶({bucket.name})对应的nextcloud信息。')
            raise exc

        # 删除 nextcloud_user_id 挂载桶的信息

        err_list_tables = []

        try:
            mount_id = self.delete_oc_external_applicable(nextcloud_user_id=nc_obj.mount_uid,
                                                          bucket_name=bucket.name)  # 查询信息
        except NextcloudDBUserError as exc:
            raise exc

        try:
            self.delete_oc_external_config(mount_id=mount_id)
        except NextcloudDBUserError as exc:
            err_list_tables.append(exc.err_data_old())

        try:
            self.delete_oc_external_options(mount_id=mount_id)
        except NextcloudDBUserError as exc:
            err_list_tables.append(exc.err_data_old())

        try:
            self.delete_oc_external_mounts(mount_id=mount_id)
        except NextcloudDBUserError as exc:
            err_list_tables.append(exc.err_data_old())

        if err_list_tables:
            raise NextcloudDBUserError(json.dumps(err_list_tables))

        try:
            nc_obj.delete()
        except Exception as e:
            exc = exceptions.BadRequest(f'nextcloud信息已删除，数据表记录删除时信息错误：{str(e)}。')
            raise exc

    def update_nextcloud_bucket_s3key(self, bucket_id, s3_key, s3_secret):

        nc_b = self.get_nextcloud_bucket(bucket_id=bucket_id)

        # 查询 oc_external_applicable 根据 value(uid) 获取 mount_id
        nc_db_config = NextCloudExternalManagement(table_name=NextCloudExternalConfig.Meta.db_table,
                                                   nextcloud_class=NextCloudExternalConfig)

        nc_db_config_class = nc_db_config.get_obj_model_class()
        nc_db_config_objs = nc_db_config_class.objects.filter(mount_id=nc_b.mount_id, key__in=['key', 'secret'])
        if not nc_db_config_objs:
            raise exceptions.BadRequest(f'nextcloud 数据库中未找到 {nc_b.bucket_name} 桶的s3密钥信息')

        for nc_obj in nc_db_config_objs:
            if nc_obj.key == 'key':
                nc_obj.value = s3_key
            if nc_obj.key == 'secret':
                nc_obj.value = s3_secret

            try:
                nc_obj.save(update_fields=['key', 'value'])
            except Exception as e:
                raise NextcloudDBError(f'{nc_b.bucket_name} 修改 nextcloud 中s3 信息失败：{str(e)}')
